<template>
  <div class="ui-player-warp" :class="{'ui-player-hide':!isShow}">
    <div class="ui-player-container">
      <div class="ui-player-body">
        <div class="ui-song-cover">
          <img :src="curSong.image?curSong.image:defaultImage" alt="" class="ui-cover-image">
          <img v-if="!isPaused&&!isError" src="../assets/wave.gif" alt="" class="ui-playing-icon">
          <span v-if="isError" class="iconfont icon-error ui-error-icon" />
        </div>
        <div class="ui-song-info">
          <div class="ui-song-infoRow">{{ curSong.title }}</div>
          <div class="ui-song-infoRow">{{ curSong.author }}</div>
          <div class="ui-song-infoRow">{{ duration | formatTime }}</div>
        </div>
        <div class="ui-player-control">
          <div class="ui-control-item">
            <span class="iconfont icon-prev" @click="prevSong" />
          </div>
          <div class="ui-control-item">
            <span
              class="iconfont ui-toggle-play"
              :class="isPaused?'icon-play':'icon-pause'"
              @click="togglePlay"
            />
          </div>
          <div class="ui-control-item">
            <span class="iconfont icon-next" @click="nextSong" />
          </div>
        </div>
        <div class="ui-volume-warp">
          <div class="ui-volume-container">
            <div class="ui-volume-icon">
              <span class="iconfont icon-voice" />
            </div>
            <div class="ui-volume-progress">
              <UiProgress :progress="volume" :sync="true" @progress-change="updateVolume" />
            </div>
          </div>
        </div>
        <div class="ui-curTime-warp">
          {{ curTime | formatTime }}
        </div>
        <div class="ui-player-menus">
          <div class="ui-player-menu-item">
            <span @click="isLyric=!isLyric">词</span>
          </div>
          <div class="ui-player-menu-item">
            <span class="iconfont icon-menu" @click="isSongs=!isSongs" />
          </div>
        </div>
      </div>
      <div class="ui-time-progress">
        <UiProgress :preload="preload" :progress="played" @progress-change="updateTime" />
      </div>
    </div>
    <div class="ui-toggle-warp" @click="isShow=!isShow">
      <span class="iconfont" :class="isShow?'icon-left':'icon-right'" />
    </div>
    <div class="ui-player-lyric" :class="{'ui-lyric-hide':!isLyric}">
      <canvas ref="canvas" class="my-canvas" width="160px" height="80px" />
      <div class="ui-lyric-bg" :style="{backgroundImage:'url('+(curSong.image?curSong.image:defaultImage)+')'}" />
      <UiLyric :source-lyric="lyric" :cur-time="curTime" :loading="searching" />
      <span class="iconfont icon-close ui-close-btn" @click="isLyric=false" />
    </div>
    <div v-show="isSongs" class="ui-songs-warp">
      <div class="ui-songs-header">
        <div class="ui-songs-title">播放列表 ({{ songs.length }})</div>
        <span class="iconfont icon-close ui-list-close-btn" @click="isSongs=false" />
      </div>
      <div ref="list" class="ui-songs-container scroll">
        <ul class="ui-song-list">
          <li v-for="(item,i) in songs" :key="i" :class="{active:item.id===curSong.id}">
            <div class="ui-song-status">
              <span class="iconfont icon-bofang" />
            </div>
            <div class="ui-song-item-info">{{ item.title }}-{{ item.author }}</div>
            <div class="ui-song-control">
              <div class="ui-song-control-item">
                <span class="iconfont icon-play" @click="play(item)" />
              </div>
              <div v-if="i!==0" class="ui-song-control-item">
                <span class="iconfont icon-up" @click="moveUp(item)" />
              </div>
              <div v-if="i!==(songs.length-1)" class="ui-song-control-item">
                <span class="iconfont icon-down" @click="moveDown(item)" />
              </div>
              <div class="ui-song-control-item">
                <span class="iconfont icon-delete" @click="delSong(item)" />
              </div>
            </div>
          </li>
        </ul>
      </div>
    </div>
    <audio ref="audio" />
  </div>
</template>
<script>
import UiProgress from './UiProgress'
import UiLyric from './UiLyric'
export default {
  name: 'UiAplayer',
  components: {
    UiProgress,
    UiLyric
  },
  filters: {
    formatTime(value) {
      let m = parseInt(value / 60)
      let s = parseInt(value % 60)
      if (m <= 9) {
        m = '0' + m
      }
      if (s <= 9) {
        s = '0' + s
      }
      return m + ':' + s
    }
  },
  data() {
    return {
      curSong: {},
      songs: [],
      isPaused: true,
      isError: false,
      curTime: 0,
      duration: 0,
      preload: 0,
      volume: 0.5,
      played: 0,
      lyric: '',
      defaultImage: 'http://p2.music.126.net/KK2fyHMit2saFMMMDUfJ3w==/109951164249516600.jpg',
      searching: false,
      isLyric: false,
      isShow: false,
      isSongs: false
    }
  },
  mounted() {
    this.initPlayer()
    if (this.songs.length === 0) {
      this.getPlayList('545888750')
    }
  },
  methods: {
    changeSong(song) {
      if (song.id === this.curSong.id) {
        return
      }
      this.curTime = 0
      this.preload = 0
      this.duration = 0
      this.played = 0
      this.isError = false
      this.$audio.src = 'https://music.163.com/song/media/outer/url?id=' + song.id + '.mp3'
      this.curSong = song
      if ('mediaSession' in navigator) {
        navigator.mediaSession.metadata = new window.MediaMetadata({
          title: song.title,
          artist: song.author,
          album: song.title,
          artwork: [
            { src: song.image, sizes: '256x256', type: 'image/jpg' }
          ]
        })
      }
      this.getLyric(song.id)
    },
    initPlayer() {
      const _this = this
      const $audio = this.$refs['audio']
      this.$audio = $audio
      $audio.volume = this.volume
      $audio.onpause = () => {
        _this.isPaused = true
      }
      $audio.onplay = () => {
        _this.isPaused = false
      }
      $audio.oncanplay = () => {}
      $audio.ontimeupdate = function() {
        const time = $audio.currentTime
        _this.curTime = time
        _this.played = time / $audio.duration
      }
      $audio.onended = function() {
        _this.nextSong()
        $audio.play()
      }
      $audio.onerror = () => {
        _this.isPaused = true
        _this.isError = true
        console.error('歌曲《' + _this.curSong.title + '》加载出错了', 'error')
      }
      $audio.onloadedmetadata = () => {
        _this.duration = $audio.duration
      }
      $audio.ondurationchange = () => {
        _this.duration = $audio.duration
      }
      $audio.onprogress = () => {
        const len = $audio.buffered.length
        if (len <= 0) {
          return
        }
        const time = $audio.buffered.end(len - 1)
        _this.preload = time / $audio.duration
      }
      if ('mediaSession' in navigator) {
        navigator.mediaSession.setActionHandler('previoustrack', () => {
          this.prevSong()
        })
        navigator.mediaSession.setActionHandler('nexttrack', () => {
          this.nextSong()
        })
      }
    },
    togglePlay() {
      if (this.isError) {
        return
      }
      if (this.$audio.paused) {
        this.$audio.play()
      } else {
        this.$audio.pause()
      }
    },
    updateTime(val) {
      this.played = val
      if (this.$audio) {
        this.$audio.currentTime = val * this.$audio.duration
      }
    },
    updateVolume(val) {
      this.volume = val
      if (this.$audio) {
        this.$audio.volume = val
      }
    },
    findIndex(playList, song) {
      let index = -1
      if (!song.id) {
        return index
      }
      playList.forEach((item, i) => {
        if (item.id === song.id) {
          index = i
        }
      })
      return index
    },
    play(song) {
      this.changeSong(song)
      this.$audio.play()
    },
    nextSong() {
      const len = this.songs.length
      const index = this.findIndex(this.songs, this.curSong)
      const isPaused = this.$audio.paused
      if (len > 0) {
        const nextIndex = index + 1
        if ((nextIndex >= 0) && (nextIndex < len)) {
          const song = this.songs[nextIndex]
          this.changeSong(song)
        } else {
          const song = this.songs[0]
          this.changeSong(song)
        }
        if (!isPaused) {
          this.$audio.play()
        }
      }
    },
    prevSong() {
      const len = this.songs.length
      const index = this.findIndex(this.songs, this.curSong)
      const isPaused = this.$audio.paused
      if (len > 0) {
        const prevIndex = index - 1
        if ((prevIndex >= 0) && (prevIndex < len)) {
          const song = this.songs[prevIndex]
          this.changeSong(song)
        } else {
          const song = this.songs[len - 1]
          this.changeSong(song)
        }
        if (!isPaused) {
          this.$audio.play()
        }
      }
    },
    moveUp(song) {
      const index = this.findIndex(this.songs, song)
      if (index !== -1) {
        const prevIndex = index - 1
        if (prevIndex >= 0) {
          const prevSong = this.songs[prevIndex]
          this.$set(this.songs, prevIndex, song)
          this.$set(this.songs, index, prevSong)
        }
      }
    },
    moveDown(song) {
      const index = this.findIndex(this.songs, song)
      const len = this.songs.length
      if (index !== -1) {
        const nextIndex = index + 1
        if (nextIndex < len) {
          const nextSong = this.songs[nextIndex]
          this.$set(this.songs, nextIndex, song)
          this.$set(this.songs, index, nextSong)
        }
      }
    },
    delSong(song) {
      const index = this.findIndex(this.songs, song)
      if (index !== -1) {
        this.songs.splice(index, 1)
      }
    },
    getLyric(id) {
      this.lyric = ''
      this.searching = true
      fetch('https://api.asilu.com/163music/?type=songlrc&lrc=lrc&id=' + id).then((response) => {
        return response.text()
      }).then((text) => {
        if (text) {
          this.lyric = text
        }
      }).finally(() => {
        this.searching = false
      })
    },
    getPlayList(listId) {
      this.$http.jsonp('https://api.asilu.com/cloud-music/163', {
        params: {
          type: 'playlist',
          id: listId
        }
      }).then((response) => {
        const list = response.body.list
        this.songs = list
        if (list.length > 0 && !this.curSong.id) {
          this.changeSong(list[0])
        }
      })
    },
    initAnalyser() {
      const audioContext = new (window.AudioContext || window.webkitAudioContext)()
      const audioElement = this.$refs['audio']
      const canvas = this.$refs['canvas']
      const canvasCtx = canvas.getContext('2d')
      const source = audioContext.createMediaElementSource(audioElement)
      const analyser = audioContext.createAnalyser()
      analyser.fftSize = 256 // 分辨率，越大频谱越细腻
      const bufferLength = analyser.frequencyBinCount
      const dataArray = new Uint8Array(bufferLength)
      source.connect(analyser)
      analyser.connect(audioContext.destination)
      function draw() {
        requestAnimationFrame(draw)
        analyser.getByteFrequencyData(dataArray)
        const width = canvas.width
        const height = canvas.height
        canvasCtx.clearRect(0, 0, canvas.width, canvas.height) // 清除画布
        canvasCtx.fillStyle = 'rgba(0,0,0)' // 设置背景色（可选）
        canvasCtx.fillRect(0, 0, width, height)
        const barWidth = (canvas.width / bufferLength)
        let barHeight
        for (let i = 0; i < bufferLength; i++) {
          barHeight = dataArray[i]
          canvasCtx.fillStyle = `rgb(255,50,50)` // 根据音量改变颜色
          const x = i * (barWidth + 1) + width / 2
          const y = height - barHeight
          const x1 = (width / 2) - (i * (barWidth + 1))
          canvasCtx.fillRect(x, y / 2, barWidth, barHeight / 2)
          canvasCtx.fillRect(x1, y / 2, barWidth, barHeight / 2)
          canvasCtx.fillRect(x, y / 2 + barHeight / 2, barWidth, barHeight / 2)
          canvasCtx.fillRect(x1, y / 2 + barHeight / 2, barWidth, barHeight / 2)
        }
      }
      draw()
    }
  }
}
</script>
<style scoped lang="less">
  @bg-color:linear-gradient(60deg, #3b424b 0%, #3d3f4d 100%);
  @hover-bg:rgba(0,0,0,0.3);
  @font-color: #c0c0c0;
  @icon-color: #b3b3b3;
  @icon-hover:#ffffff;
  .ui-player-warp{
    position: fixed;
    left: 0;
    bottom: 40px;
    transition: left .5s;
  }
  .ui-player-container{
    background-image: @bg-color;
    padding: 5px 10px;
  }
  .ui-player-body{
    height: 60px;
    position: relative;
  }
  .ui-curTime-warp{
    width: 35px;
    font-size: 12px;
    line-height: 20px;
    position: absolute;
    right: 0;
    color: @font-color;
    bottom: 0;
  }
  .ui-player-body:after{
    display: block;
    content: '';
    clear: both;
  }
  .ui-song-cover{
    width: 60px;
    height: 60px;
    position: relative;
    float: left;
  }
  .ui-playing-icon{
    display: block;
    width: 10px;
    height: 10px;
    bottom: 0;
    right: 5px;
    border: none;
    position: absolute;
  }
  .ui-error-icon{
    position: absolute;
    display: block;
    width: 16px;
    height: 16px;
    line-height: 16px;
    text-align: center;
    bottom: 0;
    right: 2px;
    border: none;
    color: #e64b15;
    background-color: #ffffff;
    border-radius: 50%;
  }
  .ui-song-cover img.ui-cover-image{
    display: block;
    width: 100%;
    height: 100%;
    border-radius: 4px;
  }
  .ui-song-info{
    float: left;
    height: 60px;
    position: relative;
    width: 80px;
    margin-left: 5px;
  }
  .ui-song-infoRow{
    position: relative;
    height: 20px;
    color: @font-color;
    font-size: 12px;
    line-height: 20px;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
  }
  .ui-player-control{
    float: left;
    height: 60px;
    padding: 10px 0;
    box-sizing: border-box;
    width: 90px;
  }
  .ui-control-item{
    float: left;
    line-height: 40px;
    height: 40px;
    width: 30px;
    text-align: center;
  }
  .ui-control-item span.iconfont{
    font-size: 20px;
    color: @icon-color;
    cursor: pointer;
  }
  .ui-control-item span.iconfont:hover{
    color: @icon-hover;
  }
  .ui-control-item span.ui-toggle-play{
    font-size: 25px;
  }
  .ui-volume-warp{
    height: 60px;
    padding: 15px 0;
    box-sizing: border-box;
    float: left;
    width: 80px;
  }
  .ui-volume-container{
    height: 30px;
    width: 100%;
  }
  .ui-volume-icon{
    float: left;
    width: 30px;
    height: 30px;
    line-height: 30px;
    text-align: center;
  }
  .ui-volume-icon span.iconfont{
    font-size: 20px;
    color: @icon-color;
  }
  .ui-volume-progress{
    margin-left: 30px;
    height: 4px;
    padding: 13px 0;
    box-sizing: content-box;
  }
  .ui-time-progress{
    height: 4px;
    padding-top: 6px;
    box-sizing: content-box;
  }
  .ui-toggle-warp{
    position: absolute;
    left: 100%;
    width: 20px;
    height: 80px;
    background-image: @bg-color;
    border-radius: 0 4px 4px 0;
    text-align: center;
    line-height: 80px;
    top: 0;
    border-left: 1px solid #333333;
    cursor: pointer;
  }
  .ui-toggle-warp span.iconfont{
    color: @icon-color;
    font-size: 14px;
  }
  .ui-toggle-warp:hover span.iconfont{
    color: @icon-hover;
  }
  .ui-player-lyric{
    position: absolute;
    left: 100%;
    height: 80px;
    width: 160px;
    border-radius: 4px;
    background-image: @bg-color;
    top: 0;
    margin-left: 25px;
  }
  .ui-player-menus{
    position: absolute;
    height: 20px;
    width: 40px;
    right: 0;
    top: 0;
  }
  .ui-player-menu-item{
    display: block;
    width: 20px;
    height: 20px;
    text-align: center;
    line-height: 20px;
    float: left;
  }
  .ui-player-menu-item span{
    font-size: 12px;
    color: @icon-color;
    cursor: pointer;
    user-select: none;
  }
  .ui-player-menu-item span:hover{
    color: @icon-hover;
  }
  .ui-lyric-hide{
    opacity: 0;
    visibility: hidden;
    z-index: -999;
  }
  .ui-player-warp.ui-player-hide{
    left: -335px;
  }
  .ui-close-btn{
    position: absolute;
    width: 20px;
    height: 20px;
    color: @icon-color;
    text-align: center;
    line-height: 20px;
    top: 0;
    right: 0;
    font-size: 12px;
    cursor: pointer;
  }
  .ui-list-close-btn{
    position: absolute;
    width: 20px;
    height: 30px;
    color: @icon-color;
    text-align: center;
    line-height: 30px;
    top: 0;
    right: 5px;
    font-size: 14px;
    cursor: pointer;
  }
  .ui-close-btn:hover,
  .ui-list-close-btn:hover{
    color: @icon-hover;
  }
  .ui-lyric-bg{
    display: block;
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    border-radius: 4px;
    background-position: center;
    background-size: cover;
    background-repeat: no-repeat;
    opacity: .2;
    filter: blur(20px);
  }
  .ui-songs-warp{
    position: absolute;
    left: 0;
    bottom: 100%;
    width: 100%;
  }
  .ui-songs-header{
    height: 30px;
    position: relative;
    background-image: @bg-color;
    border-radius: 4px 4px 0 0;
    border-bottom: 1px solid #333333;
  }
  .ui-songs-title{
    height: 30px;
    line-height: 30px;
    font-size: 14px;
    color: @font-color;
    padding-left: 10px;
  }
  .ui-songs-container{
    height: 210px;
    overflow-y: auto;
    background-image: @bg-color;
    border-bottom: 1px solid rgba(51, 51, 51, 0.71);
  }
  .ui-song-list{
    list-style: none;
    padding: 0;
    margin: 0;
  }
  .ui-song-status{
    display: block;
    float: left;
    width: 20px;
    height: 30px;
    line-height: 30px;
    opacity: 0;
  }
  .ui-song-status span.iconfont{
    font-size: 16px;
    color: #e64b15;
  }
  .ui-song-list li.active .ui-song-status{
    opacity: 1;
  }
  .ui-song-list li{
    height: 30px;
    line-height: 30px;
    position: relative;
    padding: 0 10px;
  }
  .ui-song-list li:hover,
  .ui-song-list li.active{
    background: @hover-bg;
  }
  .ui-song-item-info{
    height: 30px;
    line-height: 30px;
    margin-left: 20px;
    margin-right: 85px;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    font-size: 12px;
    color: @font-color;
  }
  .ui-song-list li:hover .ui-song-control{
    display: block;
  }
  .ui-song-control{
    height: 30px;
    position: absolute;
    width: 80px;
    right: 10px;
    top: 0;
    display: none;
  }
  .ui-song-control-item{
    float: left;
    width: 20px;
    text-align: center;
    height: 30px;
    line-height: 30px;
  }
  .ui-song-control-item span.iconfont{
    font-size: 16px;
    color: @icon-color;
    cursor: pointer;
  }
  .ui-song-control-item span.iconfont:hover{
    color: @icon-hover;
  }
  .ui-player-lyric/deep/ .ui-lyric-item{
    color: @font-color;
  }
  .ui-player-lyric/deep/ .ui-lyric-item.active{
    color: #ffa803;
  }
  /*定义滚动条宽高及背景，宽高分别对应横竖滚动条的尺寸*/
  .scroll::-webkit-scrollbar{
    width: 5px;
    height: 5px;
    background-color: rgba(255, 255, 255, 0.13);
  }
  /*定义滚动条的轨道，内阴影及圆角*/
  .scroll::-webkit-scrollbar-track{
    -webkit-box-shadow: inset 0 0 6px rgba(240, 240, 240, 0);
    border-radius: 10px;
    background-color: rgba(0, 89, 255, 0);
  }
  /*定义滑块，内阴影及圆角*/
  .scroll::-webkit-scrollbar-thumb{
    /*width: 10px;*/
    height: 20px;
    border-radius: 10px;
    -webkit-box-shadow: inset 0 0 6px rgba(236, 236, 236, 0.3);
    background-color: rgba(203, 203, 203, 0.54);
    transition: all 0.5s;
  }
  .my-canvas{
    position: absolute;
    width: 160px;
    height: 80px;
    inset: 0;
  }
</style>
